﻿#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import QWidget, QApplication, QMessageBox, QInputDialog
from PyQt5.QtGui import QPainter, QPen, QPalette, QColor
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QBasicTimer
import sys
import random
import os
from collections import deque


class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.message_box_run()  # 消息盒子获取难度信息
        self.init_ui()  # 初始化ui
        self.draw_init()  # 初始化图形
        palette1 = QPalette()  # 背景设置为黑色
        palette1.setColor(self.backgroundRole(), QColor(0, 0, 0))
        self.setPalette(palette1)

    def init_ui(self):

        self.resize(300, 300)
        self.setWindowTitle('贪吃蛇')
        self.timer = QBasicTimer()  # 时钟开启
        self.timer.start(self.global_speed, self)  # 步数为开局获取的步数
        self.show()

    def message_box_run(self):
        int_, ok = QInputDialog().getInt(QWidget(), '选择游戏难度', '输入难度1-10:', 5, 1, 10)
        if not ok:
            os._exit(0)  # 如果获取关闭则关闭游戏
        self.global_speed = (-int_ + 11)*100

    def timerEvent(self, a0) -> None:
        self.update()  # 通过时钟信号更新图像

    def paintEvent(self, e):

        self.qp = QPainter()  # 画图工具继承
        self.qp.begin(self)  # 开启
        self.drawPoints(self.qp)
        self.qp.end()

    def change_foot(self):  # 食物生成
        size = self.size()
        self.point_food = [random.randint(2, size.width() // 10 - 2) * 10,
                           random.randint(2, size.height() // 10 - 2) * 10]

    def drawPoints(self, qp):
        speed = 10  # 运行步距为10像素
        point_temp = self.point
        qp.setPen(QPen(Qt.yellow, 10))  # 设置画笔
        qp.drawPoint(self.point_food[0], self.point_food[1])  # 显示食物

        if point_temp[-1] == self.point_food:  # 对吃下去的食物进行处理
            self.point_temp_food.append(self.point_food)
            self.change_foot()  # 重新创建食物
            while self.point_food in self.point:  # 如果食物和蛇重合就重新创建直到不重合
                self.change_foot()
        # 运动轨迹，尾部减一，头部加一
        if self.key_word == Qt.Key_Right:
            point_temp.popleft()  # 左边，尾巴
            point_temp.append([(point_temp[-1][0] + speed), (point_temp[-1][1])])
            if point_temp[0] in self.point_temp_food:  # 如果发现头部和食物重合就添加在尾部
                self.point_temp_food.remove(point_temp[0])
                point_temp.append(point_temp[-1])

        elif self.key_word == Qt.Key_Left:
            point_temp.popleft()
            point_temp.append([(point_temp[-1][0] - speed), (point_temp[-1][1])])
            if point_temp[0] in self.point_temp_food:
                self.point_temp_food.remove(point_temp[0])
                point_temp.append(point_temp[-1])
        elif self.key_word == Qt.Key_Down:
            point_temp.popleft()
            point_temp.append([(point_temp[-1][0]), (point_temp[-1][1]) + speed])
            if point_temp[0] in self.point_temp_food:
                self.point_temp_food.remove(point_temp[0])
                point_temp.append(point_temp[-1])
        elif self.key_word == Qt.Key_Up:
            point_temp.popleft()
            point_temp.append([(point_temp[-1][0]), (point_temp[-1][1]) - speed])
            if point_temp[0] in self.point_temp_food:
                self.point_temp_food.remove(point_temp[0])
                point_temp.append(point_temp[-1])

        for x, y in point_temp:  # 画出蛇和食物
            qp.drawPoint(x, y)

        self.point = point_temp  # 更新蛇的样子
        self.dead_()  # 判断死亡

    def keyPressEvent(self, event):  # 按键控制
        if event.key() == Qt.Key_Right and self.key_word == Qt.Key_Left:  # 向左不能向右，反之亦然
            return
        elif event.key() == Qt.Key_Left and self.key_word == Qt.Key_Right:
            return
        elif event.key() == Qt.Key_Down and self.key_word == Qt.Key_Up:  # 向上不能向下，反之亦然
            return
        elif event.key() == Qt.Key_Up and self.key_word == Qt.Key_Down:
            return
        elif event.key() == Qt.Key_Escape:  # 如果按到esc就退出
            self.close()

        self.key_word = event.key()  # 将信号储存起来

    def draw_init(self):
        self.point = deque([[130, 150], [140, 150], [150, 150], [160, 150], [170, 150]])  # 初始长度
        self.point_food = [240, 150]  # 初始食物
        self.point_temp_food = deque([])  # 吃下去的食物
        self.key_word = Qt.Key_Right  # 按键数据
        self.qp_init = QPainter()  # 初始化绘图
        self.qp_init.begin(self)
        self.qp_init.setPen(QPen(Qt.yellow, 10))
        for x, y in self.point:
            self.qp_init.drawPoint(x, y)
        self.qp_init.end()

    def dead_(self):
        size = self.size()
        point_temp = self.point.copy()  # 浅复制，如果赋值则认为是指针
        for s in self.point_temp_food:  # 移除干扰的食物
            if s in point_temp:
                point_temp.remove(s)
        point_temp.pop()  # 移除头部
        point_temp.pop()
        if self.point[-1] in point_temp:  # 如果发现头部和身体重合就执行死亡
            self.dead_decide()
        elif self.point[-1][0] == size.width()-0 or self.point[-1][1] == size.height()-0 \
                or self.point[-1][0] == 0 or self.point[-1][1] == 0:  # 如果发现头部碰到四周墙壁就思维
            self.dead_decide()

    def dead_decide(self):
        reply = QMessageBox.question(self, 'Message', f"你死了，游戏结束。得分为{len(self.point)-5}", QMessageBox.Close |
                                     QMessageBox.Retry, QMessageBox.Retry)  # 弹出窗口游戏结算
        if reply == QMessageBox.Close:  # 信号判断
            self.close()  # 选择关闭
        else:
            self.draw_init()  # 选择重新开始

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

